package com.project.zsxd.server.clocksleep.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.project.zsxd.server.clocksleep.entity.ClockSleep;
import com.project.zsxd.server.clocksleep.mapper.ClockSleepMapper;
import com.project.zsxd.server.clocksleep.service.IClockSleepService;
import com.project.zsxd.server.clocksleep.vo.ClockSleepVO;
import com.project.zsxd.server.common.enums.ResultEnum;
import com.project.zsxd.server.common.exception.SystemException;
import com.project.zsxd.server.common.util.*;
import com.project.zsxd.server.sleepInfo.service.ISleepInfoService;
import com.project.zsxd.server.user.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import static com.project.zsxd.server.common.enums.ResultEnum.*;
import static com.project.zsxd.server.common.util.LocalDateTimeUtil.formatDate;
import static com.project.zsxd.server.common.util.LocalDateTimeUtil.getSleepDate;
import static com.project.zsxd.server.common.util.LocalDateUtil.format;

/**
 * <p>
 * 睡眠打卡表 服务实现类
 * </p>
 *
 * @author kaler
 * @since 2020-09-23
 */
@Slf4j
@Service
public class ClockSleepServiceImpl extends ServiceImpl<ClockSleepMapper, ClockSleep> implements IClockSleepService {

    @Autowired
    private ISleepInfoService sleepInfoService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ClockSleep startSleep(User user) {
        // 查询今日是否已打卡
        if (queryIsSleepping(user)) {
            LogUtil.warn("user {} has clocked.", user.getUserName());
            throw new SystemException(ResultEnum.SLEEP_CLOCKED);
        }
        // 打卡
        Date sleepTime = new Date();
        String sleepDate = DateUtil.formatDate(DateUtil.getSleepDate(sleepTime));
        return addSleep(user, sleepDate, sleepTime, false);
    }

    private ClockSleep addSleep(User user, String sleepDate, Date sleepTime, boolean isFill) {
        // 打卡
        ClockSleep clockSleep = new ClockSleep();
        clockSleep.setSleepDate(sleepDate);
        clockSleep.setSleepTime(sleepTime);
        clockSleep.setUserName(user.getUserName());
        save(clockSleep);

        // 更新打卡统计信息
        sleepInfoService.addOneClockSleep(user, isFill);
        return clockSleep;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ClockSleep fillSleep(User currentUser, ClockSleepVO clockSleepVO) {
        // 检查补打卡信息
        checkFillSleep(currentUser, clockSleepVO);
        // 补打卡
        LocalTime sleepTime = LocalTimeUtil.parse(clockSleepVO.getSleepTimeVal());
        String realSleepDate = clockSleepVO.getSleepDate();
        if (sleepTime.getHour() < 12) {
            LocalDate sleepDate = LocalDateUtil.parse(clockSleepVO.getSleepDate());
            realSleepDate = format(sleepDate.plusDays(1));
        }
        String realSleepDateTime = realSleepDate + " " + clockSleepVO.getSleepTimeVal();
        try {
            clockSleepVO.setSleepTime(DateUtil.parseTime(realSleepDateTime));
        } catch (ParseException e) {
            log.error("parseTime error." + realSleepDateTime);
            throw new SystemException(SYSTEM_ERROR);
        }
        return addSleep(currentUser, clockSleepVO.getSleepDate(), clockSleepVO.getSleepTime(), true);
    }

    /**
     * 检查补打卡信息
     * @param currentUser 用户
     * @param clockSleepVO 补打卡记录
     */
    private void checkFillSleep(User currentUser, ClockSleepVO clockSleepVO) {
        // 只能在当月补打卡
        LocalDate sleepDate = LocalDateUtil.parse(clockSleepVO.getSleepDate());
        LocalDateTime currentTime = getSleepDate(LocalDateTime.now());
        if (sleepDate.getYear() != currentTime.getYear() || sleepDate.getMonth() != currentTime.getMonth()) {
            log.error("historySleep not in current month");
            throw new SystemException(FILL_CLOCK_DATE);
        }
        // 只能补上一睡眠日以及之前的打卡记录
        LocalDate lastSleepDate = currentTime.toLocalDate().minusDays(1);
        if (sleepDate.isAfter(lastSleepDate)) {
            log.error("sleepDate isAfter lastSleepDate");
            throw new SystemException(FILL_CLOCK_DATE);
        }
        // 检查历史日期是否已打过卡
        if (queryIsSleepping(currentUser, clockSleepVO.getSleepDate())) {
            log.error("sleepDate has clocked");
            throw new SystemException(FILL_CLOCKED);
        }
    }

    /**
     * 根据传入的时间计算入睡日期 yyyy-MM-dd
     * @param sleepTime
     * @return
     */
    @Override
    public String calSleepDate(Date sleepTime) {
        Calendar sleepTimeCal = Calendar.getInstance();
        sleepTimeCal.setTime(sleepTime);
        // 当天中午12点
        Calendar middleCal = (Calendar) sleepTimeCal.clone();
        middleCal.set(Calendar.HOUR_OF_DAY, 12);
        middleCal.set(Calendar.MINUTE, 0);
        middleCal.set(Calendar.SECOND, 0);
        middleCal.set(Calendar.MILLISECOND, 0);
        // 如果入睡时间<中午12点，则记为前一天;否则就是当天
        Calendar sleepDateCal = (Calendar) sleepTimeCal.clone();
        if (sleepTime.before(middleCal.getTime())) {
            sleepDateCal.add(Calendar.DAY_OF_MONTH, -1);
        }
        return DateUtil.formatDate(sleepDateCal.getTime());
    }

    @Override
    public List<ClockSleepVO> queryLastMonthSleepTimes() {
        User currentUser = (User) SecurityUtils.getSubject().getPrincipal();
        List<ClockSleepVO> clockSleepList = baseMapper.queryLastMonthSleepTimes(currentUser.getUserName());
        if (!clockSleepList.isEmpty()) {
            for (ClockSleepVO vo : clockSleepList) {
                vo.setSleepTimeVal(DateUtil.convertSleepTime(vo.getSleepTime()));
            }
        }
        return clockSleepList;
    }

    @Override
    public JSONObject queryLastMonthSleepTimesOld() {
        User currentUser = (User) SecurityUtils.getSubject().getPrincipal();
        QueryWrapper<ClockSleep> queryWrapper = new QueryWrapper<ClockSleep>().eq("user_name", currentUser.getUserName())
                .orderByDesc("sleep_time");
        IPage<ClockSleep> page = new Page<>();
        page.setSize(30);
        page = page(page, queryWrapper);
        JSONObject pageJsonObj = (JSONObject) JSON.toJSON(page);
        // 处理入睡时间
        if (page.getTotal() > 0) {
            JSONObject sleepObj;
            for (int i = 0; i < pageJsonObj.getJSONArray("records").size(); i++) {
                sleepObj = pageJsonObj.getJSONArray("records").getJSONObject(i);
                sleepObj.put("sleepTimeVal", DateUtil.convertSleepTime(sleepObj.getString("sleepTime")));
            }
        }
        return pageJsonObj;
    }

    @Override
    public boolean queryIsSleepping(User user) {
        String sleepDate = formatDate(getSleepDate(LocalDateTime.now()));
        return queryIsSleepping(user, sleepDate);
    }

    @Override
    public boolean queryIsSleepping(User user, String sleepDate) {
        LambdaQueryWrapper<ClockSleep> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ClockSleep::getUserName, user.getUserName())
                .eq(ClockSleep::getSleepDate, sleepDate);
        int count = this.count(queryWrapper);
        return count > 0;
    }
}
